<?php
/**
 * @author		João Batista Neto
 * @brief		Classes e interfaces utilitárias
 * @package		rpo.util
 */

require_once 'rpo/util/ObjectIterator.php';

/**
 * @brief	Implementação simples de um Iterator recursivo de Iteratores
 * @class	RecursiveObjectIteratorIterator
 */
abstract class RecursiveObjectIteratorIterator extends ObjectIterator {
	/**
	 * @brief	Nível de profundidade
	 * @var		integer
	 */
	private $level = 0;

	/**
	 * @brief	Iterator atual
	 * @var		RecursiveObjectIterator
	 */
	private $currentIterator;

	/**
	 * @brief	Constroi o iterator recursivo de componentes
	 * @param	$iterator RecursiveObjectIterator
	 */
	public function __construct( RecursiveObjectIterator $iterator ){
		parent::__construct();

		$this->currentIterator = $iterator;
		$this->append( $iterator );
	}

	/**
	 * @brief	Adiciona um novo objeto ao Iterator
	 * @param	$object IObject
	 * @throws	LogicException Se mesmo após o método accept() um objeto que não seja
	 * um RecursiveObjectIterator for recebido
	 */
	final public function append( IObject $object ){
		parent::append( $object );

		$reflection = $object->getClass();

		if ( !$reflection->isSubclassOf( 'RecursiveObjectIterator' ) ){
			throw new LogicException( sprintf( 'Apenas RecursiveObjectIterator são aceitos, %s foi dado.' , $reflection->getName() ) );
		}
	}

	/**
	 * @brief	Recupera o item atual
	 * @return	IObject
	 * @see		rpo.util.ObjectIterator::current()
	 */
	public function current(){
		return $this->currentIterator->current();
	}

	/**
	 * @brief	Recupera a profundidade do Iterator atual
	 * @return	integer
	 */
	public function getDepth(){
		return $this->level;
	}

	/**
	 * @brief	Recupera a chave do item atual
	 * @return	string
	 * @see		rpo.util.ObjectIterator.key()
	 */
	public function key(){
		return $this->currentIterator->key();
	}

	/**
	 * @brief	Avança ao próximo item do Iterator.
	 * @details	No caso do iterator recursivo de iteratores, é utilizado o método de interface hasChildren()
	 * para verificar se o item atual possui filhos, se possuir o método getChildren() é utilizado para
	 * recuperar esses filhos. O Iterator passará a iterar os filhos de um item até o fim. Quando todos
	 * os filhos do Iterator tiverem terminado, o Iterator voltará ao nível anterior e avançará para o
	 * próximo nível hierárquico.
	 * @see		rpo.util.ObjectIterator.next()
	 */
	public function next(){
		if ( $this->currentIterator->hasChildren() ){
			$newIterator = $this->currentIterator->getChildren();

			$this->append( $newIterator );
			$this->currentIterator = $newIterator;

			++$this->level;
		} else {
			$this->currentIterator->next();

			while ( !$this->currentIterator->valid() && $this->level ){
				$this->seek( --$this->level );

				$this->currentIterator = $this->storage->current();
				$this->currentIterator->next();
			}
		}
	}

	/**
	 * @brief	Reinicia o Iterator
	 * @see		rpo.util.ObjectIterator.rewind()
	 */
	public function rewind(){
		while ( $this->level ){
			$this->seek( $this->level );
			$this->storage->current()->rewind();
			$this->offsetUnset( $this->level-- );
		}

		$this->storage->rewind();
		$this->currentIterator = $this->storage->current();
		$this->currentIterator->rewind();
	}

	/**
	 * @brief	Verifica se o Iterator ainda é válido
	 * @return	boolean
	 * @see		rpo.util.ObjectIterator.valid()
	 */
	public function valid(){
		if ( $this->currentIterator->valid() ){
			return true;
		} else {
			foreach ( $this->storage as $iterator ){
				if ( $iterator->valid() ){
					return true;
				}
			}
		}

		return false;
	}
}